/*
* GeoVISTA Center (Penn State, Dept. of Geography)
* Copyright (c), 2003-2011,  Jin Chen and Alan M. MacEachren, GeoVISTA Center, Penn State University
* All Rights Researved.
*
* Functions
*   - act as a ruler to measure value
*   - control how data carried by the axis is viewed by operating its range attributes
*
* @author: jin Chen
* @date: Jun 14, 2003
* @version: $VERSION$
*/
package edu.psu.geovista.app.plot.axis;

import edu.psu.geovista.app.plot.ruler.DfRuler;
import edu.psu.geovista.app.plot.ruler.Ruler;
import edu.psu.geovista.app.plot.ruler.tick.Tick;

import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import java.awt.*;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.Arrays;

public abstract class BasicAxis extends JComponent{
    //The property name for propertyChangeSupport ->
    public static final String PShowRange="showRange";
    public static final String PTickRange="tickRange";
    public static final String ActiveSize="ActiveSize";
    //The property name for propertyChangeSupport <-


    public static final boolean DEBUG=false;
    private static final String uiClassID = "BasicAxisUI";

    static {//register PcpUI
        // UIManager.put(<UI Class ID>, <UI Class's full package name>");

        UIManager.put("BasicAxisUI", "edu.psu.geovista.app.plot.axis.BasicAxisUI");
    }
    //public static final int ORIENT_X=1;
    //public static final int ORIENT_Y=2;
    //Logic
    /**
     * valueAt0 is  the  real data value that on the O point of drawing coordinate.
     * By default, it is max of tickRange.max. If make axis up side down, the valueAt0=tickRange.min
     */
    //protected float valueAt0;   //ypixel= =y0 *ks - RawY*ks
    //protected int orient =Ruler.VERTICAL ;//Horizontal or Vertical. ??? Need to put into dataModel as change to it will change the View
    protected  BasicAxisModel model=new BasicAxisModel(); //

   // protected boolean ascend;//??? need to move into UI. Display real value on the axis.If true, value on top <value on botton.

    // 
    protected Point axisLocation=new Point(0,0);//

    protected String name="No defined";
    protected String description="";//Berief description of the axis.E.g.: full description of its name or timevalue
    protected boolean visible=true;// 
    private boolean showFilterTick=true;

    protected int totalSize;// =activesize+footersize+headersize

    protected Ruler ruler;

    //protected Ruler ui;
    protected PropertyChangeSupport pcs; //no need this one,JComponent has one. Still keep it to remind you this


    public BasicAxis() {

        this(Ruler.VERTICAL ,new Range(10,0),100,null);

    }
    public BasicAxis(int orient){
        this(orient,new Range(10,0),100,null);
    }
    public BasicAxis(BasicAxisModel model){
        //this.model =model;

        this(edu.psu.geovista.app.plot.ruler.Ruler.VERTICAL ,model,new Range(10,0),100,null);
    }
    public BasicAxis(int orient, Range dataRange, int activeSize, String axisName) {
        this(orient,null,dataRange,activeSize,axisName);


    }
    public BasicAxis(int orient, BasicAxisModel model,Range dataRange, int activeSize, String axisName) {
        //init();
        if (model!=null){
            this.model = model;
        }
        else{
            if(this.model ==null)
                model=new BasicAxisModel();
        }

        this.setOrient(orient);
        this.setDataRange(dataRange);
        this.setTickRange( dataRange);//default, show all
        this.setShowRange(dataRange); //default, show all
        if (axisName!=null)this.setName(axisName);
        if (activeSize>0)this.setActiveSize(activeSize) ;  //Instead of totalSize, accept activeSize to avoid impact of changes on header/footer on activeSize
        init();

        this.updateUI() ;



    }
    private void init(){
         this.ruler =new DfRuler(this.getOrient() ,getActiveSize(), getAxisLocation() ,getTickRange());
    }



    /*******************************************************************************************************
     *                operation
     *******************************************************************************************************/
    public float getGraphicValueOf(float realValue){
        // return ((BasicAxisUI)this.ui).getGraphicValueOf(realValue);
        // 
        return this.getRuler().getGraphicValueOf(realValue);

    }
    //Give graphic value, return real value
    public float getValueOf(int gv){
        return ((BasicAxisUI)this.ui).getValueOf(gv);
    }
    /**
     *
     * @param deltaV   change of graphic value
     * @return         change of real value correpsonding to deltaV
     */
    public float getValueOfchange(float deltaV) {
        return ((BasicAxisUI)this.ui).getValueOfchange(deltaV);
    }
    /*******************************************************************************************************
     *                UI
     *******************************************************************************************************/
    public void paintComponent(Graphics g){
        if(this.isVisible() ) {
            //resetAxisLocation() ;  //todo temp removed  
            super.paintComponent(g);
        }
        //resetAxisLocation() ;
        //ui.paintComponent(g, this);
    }
    /**
     * Resets the UI property to a value from the current look and feel.
     *
     * @see JComponent#updateUI
     */
    public void updateUI() {
        //updateLabelUIs();
        //setUI(new BasicAxisUI(this));
        setUI((BasicAxisUI)UIManager.getUI(this));
    }



    public ComponentUI getUI() {
        return ui;
    }


    /**
     * Sets the UI object which implements the L&F for this component.
     *
     * @param ui the YAxisPainter L&F object
     */
    public void setUI(BasicAxisUI ui) {
        super.setUI(ui);

    }
    /**
     * Returns the name of the L&F class that renders this component.
     *
      * @return "YAxisPainter"
     * @see JComponent#getUIClassID
     * @see UIDefaults#getUI
     */
    public String getUIClassID() {
        return uiClassID;
    }


    /********************************************************************
     *                axis location : different from component's location
     ********************************************************************/
    // Axis loc have different meaning from JComponent's loc
    public void setAxisLocation(int x, int y) {
        //this.getAxisLocation().setLocation(x,y) ;
        model.setAxisLocation(x,y);
        this.resetLocation();
        this.model.fireStateChanged();

    }
    // 
    /**
     * Have different meaning as JComponent, don't touch it
     * To setup axis location, use setAxisLocation() instead
     */
    public void setLocation(int x, int y) {
        super.setLocation(x,y);
        //dp{
        if (DEBUG){
            System.out.println(this.getName() +"size:"+this.getSize() );
            System.out.println(this.getName() +"location:"+this.getLocation() );

        }//dp}
        this.resetAxisLocation();


    }
    public Point getLocation(){
      /*  Point loc = super.getLocation();
        if(!loc.equals(this.getAxisLocation() ) ){
            System.out.println("not equal");
        }
        return loc;*/
        return super.getLocation() ;
    }
    protected  void resetLocation() {
        Point axisLocation=this.getAxisLocation() ;
        if (this.getOrient() ==Ruler.VERTICAL ){
            int x=axisLocation.x-this.getSize().width /2;
            int y=axisLocation.y;
            super.setLocation(x,y);
        }
        else if (this.getOrient() ==Ruler.HORIZONTAL ){
            int x=axisLocation.x;
            int y=axisLocation.y-this.getSize().height /2;
            super.setLocation(x,y);
            //System.out.println(this+" Not finish implement yet");
        }
    }
    protected  void resetAxisLocation() {
        int x=0, y=0;
        if (this.getOrient() ==Ruler.VERTICAL ){
            x=this.getLocation().x+this.getSize().width /2;
            //y=this.axisLocation.y;
            y=this.getLocation().y;

        }
        else if (this.getOrient() ==Ruler.HORIZONTAL ){
            //x=this.axisLocation.x;
            x=this.getLocation().x;
            y=this.getLocation().y+this.getSize().height /2;

            //System.out.println(this+" Not finish implement yet");
        }
        /*this.getAxisLocation().x=x;
        this.getAxisLocation().y=y;*/
        this.model.setAxisLocation(x,y);
    }
    /*public Point getAxisLocation() {
        if (this.axisLocation ==null)
            this.axisLocation =new Point();
        return axisLocation;
    }*/
    public Point getAxisLocation() {
        return this.model.getAxisLocation() ;
    }

    /*******************************************************************************************************
     *                properties
     *******************************************************************************************************/
    /*public void addPropertyChangeListener(PropertyChangeListener l) {
        pcs.addPropertyChangeListener(l);
    }
    public void removePropertyChangeListener(PropertyChangeListener l) {
        pcs.removePropertyChangeListener(l);
    }*/


    public void setActiveSize(int activeSize) {
        int old=this.getModel().activeSize;

        if (this.getModel().activeSize ==activeSize) return;
        this.getModel().setActiveSize(activeSize);

        // 
        //  
        this.firePropertyChange(BasicAxis.ActiveSize,old,activeSize);

    }
    // 
    public void addPropertyChangeListener(PropertyChangeListener l) {
        super.addPropertyChangeListener(l);

    }
    public int getActiveSize() {
        return getModel().activeSize;
    }

    public Range getTickRange() {
        return this.getModel().getTickRange() ;
    }
    public void setTickRange(Range tickRange) {
        /*if (DEBUG){
            if(tickRange.getMin() ==0){
                System.out.println("0");
            }
        }*/
        if (this.getModel().getTickRange()==null
                ||this.getModel().getTickRange().equal(tickRange))
            return;

        this.getModel().setTickRange(tickRange) ;


    }
    public void setTicksvalue(float[] ticksvalue) {

        Arrays.sort(ticksvalue);

        float min = ticksvalue[0];
        float max = ticksvalue[ticksvalue.length -1];
        this.setTickRange(new Range(max,min)); // 
        this.ruler.setTicksvalue(ticksvalue);// 
    }
    public Range getDataRange() {
        return this.getModel().getDataRange() ;
    }

    public void setDataRange(Range dataRange) {
        this.getModel().setDataRange(dataRange);
    }

    public void setShowRange(Range showRange) {
        this.getModel().setShowRange(showRange);
    }
    /**
     * make the method protected. means U must determine the orient of the axis at very begining
     * U can't change it after instantiated Axis
     * @param orient
     */
    protected  void setOrient(int orient) {
        this.getModel().setOrient(orient);
    }
    public Range getShowRange() {
        return this.getModel().getShowRange() ;
    }



    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isVisible() {
        return visible;
    }

    public void setVisible(boolean visible) {
        this.visible = visible;
    }

    public int getOrient() {
        return this.model.getOrient() ;
    }


    public BasicAxisModel getModel() {
        return model;
    }

    public   Ruler getRuler() {
        /*if(ruler==null) {
            ruler=new DfRuler();
        }*/
        return this.ruler ;
        //return((BasicAxisUI)getUI()).getRuler() ;
    }
    /**
     * u can set a ruler, and u are responsible for update ( e.g.: repaint) axis
     * @param r
     */
    public void setRuler(Ruler r) {
        this.ruler =r;
        r.setOrient(this.getOrient() );// 

    }

    public void setSize(int w, int h) {
        super.setSize(w,h);
    }
    public void setSize(Dimension d) {
        super.setSize(d);
        //super.setBounds()
    }
    /**
     *  call when resize window
     * @param x
     * @param y
     * @param w
     * @param h
     */
    public void setBounds(int x, int y, int w, int h) {
        super.setBounds(x, y, w, h);
    }
    public boolean isInShowRange(float value){
        Range sr=this.getShowRange();
        if(value<=sr.max &&value>=sr.min ){
            return true;
        }
        else{
            return false;
        }
    }

     public int getLabelScale() {
        return this.getRuler().getLabelScale() ;
    }

    public void setLabelScale(int labelScale) {
        this.getRuler().setLabelScale(labelScale);

    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
    public void setLabelFormat(int format) {
       Ruler r=this.getRuler();
       r.setFormat(format);
    }
    // 
     public void setLabelColor(Color labelColor) {
        this.ruler.setLabelColor(labelColor);
    }

    public void setLabelFont(Font labelFont) {
        this.ruler.setLabelFont(labelFont);
    }
    public void setColor(Color color){
        this.ruler.setColor(color);
    }

    public Tick[] getTicks(){
        return this.ruler.getTicks();
    }
    public boolean isShowFilterTick() {
        return showFilterTick;
    }

    public void setShowFilterTick(boolean showFilterTick) {
        this.showFilterTick = showFilterTick;
    }

}


